#include "inputtext.h"
#include "ui_inputtext.h"
#include <vector>
#include <sstream>
#include "datakeyboard.h"
#include <QDebug>

using namespace std;

InputText::InputText(QWidget *parent) :
    QDialog (parent), ui(new Ui::InputText)
{
    ui->setupUi(this);
    m_view = new PadGraphicsView;
    ui->vLayout->addWidget(m_view);
    initText();
    underStr = "  ";
    wordPredictor = new WordPredictor;

    timeCounter = new QTimer();
    timeCounter->setInterval(10);
    this->passedSecs = 0.0;
    ui->timeLabel->setVisible(0);
    this->hasStartTime = false;

    connect(ui->timeStartButton, SIGNAL(clicked()), this, SLOT(startTimeCounter()));
    connect(this->timeCounter, SIGNAL(timeout()), this, SLOT(updateTimeLabel()));

#ifdef LOG_TIME
    m_timer = new QTimer();
    m_timer->setInterval(1000 * 60);
    m_timer->start();
    connect(this->m_timer, SIGNAL(timeout()), this, SLOT(logTime()));
#endif
    connect(wordPredictor->dataKeyboard(), SIGNAL(keyTapped(float, float)), m_view, SLOT(acceptOneTap(float, float)));
    connect(wordPredictor, SIGNAL(hintWordChanged(int)), this, SLOT(highLightWord(int)));
    connect(wordPredictor, SIGNAL(candidateWordsChanged()), this, SLOT(updateCandidateWords()));
    connect(wordPredictor, SIGNAL(oneWordInputed()), this, SLOT(inputOneWord()));
    connect(ui->spaceButton, SIGNAL(clicked()), wordPredictor, SLOT(spaceButtonClicked()));
    connect(ui->deleteButton, SIGNAL(clicked()), wordPredictor, SLOT(deleteOneChar()));
    //connect(dataKeyboard, SIGNAL(keyTapped(float, float)), this, SLOT(receiveOneTap(float, float)));
    //connect(wordPredictor->m_datakeyboard, SIGNAL(keyTapped(float, float)), m_view, SLOT(acceptOneTap(float, float)));
    //dataKeyboard->start();
}

void InputText::startTimeCounter()
{
    if(this->hasStartTime == false) {
        this->startClockTime = std::chrono::high_resolution_clock::now();
        this->timeCounter->start();
        ui->timeLabel->setVisible(true);
        this->hasStartTime = true;
    }
}

void InputText::stopTimeCounter()
{
    this->timeCounter->stop();
}

void InputText::updateTimeLabel()
{
    auto nowTime = std::chrono::high_resolution_clock::now();
    auto passMs = std::chrono::duration_cast<std::chrono::milliseconds>(nowTime - this->startClockTime).count();
    this->passedSecs = passMs * 1.0 / 1000;
//    this->passedSecs += 0.01;

    if(this->passedSecs >= 60.0 - (1e-8)) {
        this->stopTimeCounter();
        this->passedSecs = 60.0;
    }
    std::string tempTimeStr = "Time: " + std::to_string(this->passedSecs) + " s";
    ui->timeLabel->setText(tempTimeStr.c_str());
}

#ifdef LOG_TIME

void InputText::logTime()
{
    static long long minNum = 0;
    qDebug() <<  ++minNum << " min has passed";
}



#endif

static vector<string> &split(const string &str, char delim, vector<string> &elems, bool skip_empty = true) {
    istringstream iss(str);
    for (string item; getline(iss, item, delim); )
        if (skip_empty && item.empty()) continue;
        else elems.push_back(item);
    return elems;
}




void InputText::initText()
{
    //FILE* fp = fopen("/Users/fanqu/programs/cv/data/textdata/datatext1.txt", "r");
//    FILE* fp = fopen("/Users/fanqu/programs/cv/data/textdata0/mackenzie.txt", "r");
    FILE* fp = fopen(Constants::mackenziePath, "r");
    outputFile = fopen(Constants::trainDataPath, "w");
    if(fp == NULL || outputFile == nullptr)
        throw "Can not open datatext";
    else {

        while(fgets(this->text, max_text_len, fp)) {
            this->sentences.push_back(this->text);
            this->words.clear();
            split(string(this->text), ' ', this->words);
            int tempNum = this->words.size();
            this->senWordNum.push_back(tempNum);
            wordNum += tempNum;
            memset(this->text, 0, sizeof(this->text));
        }
        wordIndex = 0;
        lastWordIndex = 0;
        totalChar = 0;
        curChar = 0;
        senIndex = 0;
        senWordIndex = 0;
        /*
        fgets(this->text, max_text_len, fp);

        split(string(this->text), ' ', this->words);

        wordNum = this->words.size();

        for(int i = 0; i < words.size(); ++i)
            totalChar += words[i].length();
            */
    }
    showWords();

}



void InputText::highLightWord(int index)
{
    updateCandidateWords();
    std::string styleSheet = "background-color : gray; color : white; border: 1px solid rgb(255,255,255);font-family:courier; font-size:";
    styleSheet += std::to_string(getFontSize()) + "px;";
    if(wordPredictor->getHintWords().size() > 0) {
        switch (index) {
            case 0 : ui->hintLabel1->setStyleSheet(styleSheet.c_str());
                     break;
            case 1 : ui->hintLabel2->setStyleSheet(styleSheet.c_str());
                     break;
            case 2 : ui->hintLabel3->setStyleSheet(styleSheet.c_str());
                     break;
            case 3 : ui->hintLabel4->setStyleSheet(styleSheet.c_str());
                     break;
            case 4 : ui->hintLabel5->setStyleSheet(styleSheet.c_str());
                     break;
        }
    }
}

inline int min(int x, int y) {return x < y ? x : y;}

int InputText::getFontSize()
{
    //qDebug() << "init font size";
    std::vector<std::string> tempWords = wordPredictor->getHintWords();
    int fontSize = 13;
    if(tempWords.size() > 0 && tempWords[0].length() > 0) {
        size_t wordLen = tempWords[0].length();
        if(wordLen < 7)
            fontSize = 50 - 2 * wordLen;
        else if(wordLen < 11)
            fontSize = 56 - 3 * wordLen;
        else fontSize = 33 - wordLen;
        //fontSize = min(50 - 2 * tempWords[0].length(), 50L);
    }
    //qDebug() << "font-size:" << fontSize;
    return fontSize;
}

void InputText::updateCandidateWords()
{
    std::vector<std::string> tempWords = wordPredictor->getHintWords();
    if(tempWords.size() > 0)
        ui->hintLabel1->setText(tempWords[0].c_str());
    else ui->hintLabel1->setText("");
    if(tempWords.size() > 1)
        ui->hintLabel2->setText(tempWords[1].c_str());
    else ui->hintLabel2->setText("");
    if(tempWords.size() > 2)
        ui->hintLabel3->setText(tempWords[2].c_str());
    else ui->hintLabel3->setText("");
    if(tempWords.size() > 3)
        ui->hintLabel4->setText(tempWords[3].c_str());
    else ui->hintLabel4->setText("");
    if(tempWords.size() > 4)
        ui->hintLabel5->setText(tempWords[4].c_str());
    else ui->hintLabel5->setText("");
    std::string styleSheet = "background-color : gray; color : white; font-family:courier;font-size:";
    styleSheet += std::to_string(getFontSize()) + "px;";
    ui->hintLabel1->setStyleSheet(styleSheet.c_str());
    ui->hintLabel2->setStyleSheet(styleSheet.c_str());
    ui->hintLabel3->setStyleSheet(styleSheet.c_str());
    ui->hintLabel4->setStyleSheet(styleSheet.c_str());
    ui->hintLabel5->setStyleSheet(styleSheet.c_str());
}


void InputText::showWords()
{
    senWordIndex = 0;
    size_t remainNum = wordNum - wordIndex;
    std::string showText = "  ";
#ifdef USE_TEXT
    for(size_t i = 0; i < min(remainNum, (size_t)6); ++i) {
        showText += words[wordIndex+i] + " ";
        lastWordIndex = wordIndex + i;
    }
#else
    showText += sentences[senIndex];
    lastWordIndex = wordIndex + senWordNum[senIndex] - 1;
#endif
    ui->textLabel1->setText(showText.c_str());
    ui->textLabel2->setText(underStr.c_str());
    updateChar();
}

void InputText::updateChar()
{
    std::string labelString = "Num of chars: " + std::to_string(totalChar);
    ui->charNumLabel->setText(labelString.c_str());
}

void InputText::inputOneWord()
{
    wordIndex++;
    senWordIndex++;
    totalChar += wordPredictor->lastInputWord().length();
    underStr += wordPredictor->lastInputWord();
    this->startTimeCounter();
    if(wordIndex == wordNum) {
        wordPredictor->dataKeyboard()->requestInterruption();
    }
    else {
        //qDebug() << "senWordIndex " << senWordIndex << "senwordNUm[" << senIndex << "] " << senWordNum[senIndex];
#ifdef USE_TEXT
        if(wordIndex == lastWordIndex + 1) {
#else
        if(senWordIndex == senWordNum[senIndex]) {
#endif
            underStr = "  ";
            senIndex++;
            showWords();
        }
        else {
            underStr += " ";
        }
    }
    ui->textLabel2->setText(underStr.c_str());
    updateChar();
}

InputText::~InputText()
{
    delete ui;
}
